﻿using System;
using System.Collections;
using System.Linq;
using System.Reflection;
using System.Text;
using PrintProperties.MGLHPM.Entity;

namespace PrintProperties.MGLHPM
{
    /// <summary>
    /// Class that allows the printing of entity properties.
    /// </summary>
    public class Print
    {
        /// <summary>
        /// Format for variables.
        /// </summary>
        public Format Format { get; set; }

        /// <summary>
        /// Constant to null object = null
        /// </summary>
        private const string NullObject = "null";

        /// <summary>
        /// Constant to Collection Separator = ; 
        /// </summary>
        private const string CollectionSeparator = "; ";

        /// <summary>
        /// Constant to Empty Collection = empty
        /// </summary>
        private const string EmptyCollection = "empty";

        /// <summary>
        /// Gets all properties in the entity and returns a string in the format specified.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <returns></returns>
        public string WriteEntity<T>(T obj)
        {
            InitFormat();
            if (obj.Equals(null))
            {
                return NullObject;
            }

            StringBuilder result = new StringBuilder();
            
            foreach (PropertyInfo descriptor in obj.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
            {
                string name = descriptor.Name;
                object value = descriptor.GetValue(obj,null);
                Type propertyType = descriptor.PropertyType;

                string strValue;
                if (value == null)
                {
                    strValue = NullObject;
                } else if (propertyType == typeof(DateTime))
                {
                    strValue = String.Format(Format.DateTime, (DateTime)value);
                }
                else if (IsNumeric(value))
                {
                    strValue = String.Format(Format.Number, value);
                }
                else if (propertyType == typeof(string))
                {
                    strValue = String.Format(Format.String, value);
                }
                else if (propertyType == typeof(bool))
                {
                    strValue = String.Format(Format.Boolean, value);
                }
                else if (propertyType.BaseType != null && propertyType.BaseType.Name.StartsWith("List"))
                {
                    string strList = string.Empty;
                    IEnumerable iEnum = (IEnumerable) value;
                    var list = iEnum.Cast<object>().ToList();
                    foreach (Object item in list)
                    {
                        strList += item + CollectionSeparator;
                    }

                    if (!strList.Trim().Equals(string.Empty) && strList.Length > CollectionSeparator.Length)
                    {
                        strList = strList.Substring(0, strList.Length - CollectionSeparator.Length);
                    }else
                    {
                        strList = EmptyCollection;
                    }

                    strValue = String.Format(Format.Collection, strList, propertyType.Name);
                }
                else if (propertyType.IsArray)
                {
                    string strArray = string.Empty;
                    var array = ((IEnumerable)value).Cast<object>().ToArray();
                    
                    foreach (Object item in array)
                    {
                        strArray += item.ToString() + CollectionSeparator;
                    }

                    if (!strArray.Trim().Equals(string.Empty) && strArray.Length > CollectionSeparator.Length)
                    {
                        strArray = strArray.Substring(0, strArray.Length - CollectionSeparator.Length);
                    }
                    else
                    {
                        strArray = EmptyCollection;
                    }

                    strValue = String.Format(Format.Collection, strArray, propertyType.Name);
                }
                else if(propertyType.IsClass)
                {
                    strValue = value.ToString();
                }
                else
                {
                    strValue = value.ToString();
                }

                result.Append(String.Format(Format.Property, name, strValue));
            }

            string strResult = result.ToString();

            if (!string.Empty.Equals(strResult) && strResult.Length > 2)
            {
                strResult = strResult.Substring(0, strResult.Length - 2);
            }

            strResult = String.Format(Format.Entity, strResult, obj.GetType().Name);

            return strResult;
        }

        /// <summary>
        /// Init the format to use
        /// </summary>
        private void InitFormat()
        {
            if (Format == null)
            {
                Format = new Format();
            }
            
            if (string.Empty.Equals(Format.DateTime))
            {
                Format.DateTime = Format.DefaultDatetimeFormat;
            }
            else if (string.Empty.Equals(Format.Number))
            {
                Format.Number = Format.DefaultNumberFormat;
            }
            else if (string.Empty.Equals(Format.String))
            {
                Format.String = Format.DefaultStringFormat;
            }
            else if (string.Empty.Equals(Format.Boolean))
            {
                Format.Boolean = Format.DefaultBooleanFormat;
            }
            else if (string.Empty.Equals(Format.Entity))
            {
                Format.Entity = Format.DefaultEntityFormat;
            }
            else if (string.Empty.Equals(Format.Collection))
            {
                Format.Collection = Format.DefaultCollectionFormat;
            }
            else if (string.Empty.Equals(Format.Property))
            {
                Format.Property = Format.DefaultPropertyFormat;
            }
        }

        /// <summary>
        /// Returns whether an object is numeric or not
        /// </summary>
        /// object to check
        /// <returns>boolean true/false</returns>
        private bool IsNumeric(object o)
        {
            if (o is IConvertible)
            {
                TypeCode tc = ((IConvertible)o).GetTypeCode();
                if (TypeCode.Char <= tc && tc <= TypeCode.Decimal)
                    return true;
            }
            return false;
        }

    }
}